package cn.tedu.charging.device.dao.repository.impl;

import cn.tedu.charging.common.pojo.query.NearStationsQuery;
import cn.tedu.charging.device.dao.mapper.GunMapper;
import cn.tedu.charging.device.dao.mapper.StationMapper;
import cn.tedu.charging.device.dao.repository.DeviceRepository;
import cn.tedu.charging.device.pojo.po.ChargingGunInfoPO;
import cn.tedu.charging.device.pojo.po.ChargingStationPO;
import cn.tedu.charging.device.pojo.po.StationCanalPO;
import cn.tedu.charging.device.pojo.po.StationGeoPO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.GeoOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Repository;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

@Repository
@Slf4j
public class DeviceRepositoryImpl implements DeviceRepository {
    @Autowired
    private StationMapper stationMapper;
    @Autowired
    private GunMapper gunMapper;
    @Autowired
    @Qualifier("redisTemplate")
    private RedisTemplate redisTempalte;
    @Override
    public Boolean geoStationsExists() {
        //1.在redis中存在一个命令 exists 可以根据你传递的key值 判断是否存在
        //exists charging:all:stations.geo
        return redisTempalte.hasKey("charging:all:stations.geo");
    }

    @Override
    public Long countStations() {
        //select count(0) from station
        return stationMapper.selectCount(null);
    }

    @Override
    public List<ChargingStationPO> getStationsPage(Long start, Long batch) {
        //select * from station limit start,batch
        QueryWrapper<ChargingStationPO> queryWrapper = new QueryWrapper<>();
        //允许我们在sql最后添加组织你自定义的sql内容
        queryWrapper.last("limit "+start+","+batch);
        return stationMapper.selectList(queryWrapper);
    }
    @Override
    public List<ChargingStationPO> getAllStations() {
        //select * from station
        return stationMapper.selectList(null);
    }

    @Override
    public void saveGeos(List<ChargingStationPO> pos) {
        log.debug("场站数据非空,开始写入geo,stations:{}",pos);
        //1.操作的redis的数据肯定是geo key charging:all:stations.geo value id
        GeoOperations<String,Integer> geoOps = redisTempalte.opsForGeo();
        //2.redis命令
        //geoadd charging:all:stations.geo lng1 lat1 id1 lng2 lat2 id2 ...
        //将pos这个数据库读出来的数据集合 转化成可以写入redis geo元素集合
        List<RedisGeoCommands.GeoLocation<Integer>> geos=new ArrayList<>();
        //将pos转化 做遍历循环 每循环一次,把geoLocation封装
        for (ChargingStationPO po : pos) {
            //从场站po中拿到该场站的经度
            Double lng=po.getStationLng().doubleValue();
            //拿到纬度
            Double lat=po.getStationLat().doubleValue();
            //在geo中存放大元素值 就是场站id
            Integer id = po.getId();
            //使用坐标和元素 场站id 封装geoLocation
            RedisGeoCommands.GeoLocation<Integer> location=
                    new RedisGeoCommands.GeoLocation<>(id,new Point(lng,lat));
            //添加到写入geo结合的geos对象里
            geos.add(location);
        }
        log.debug("pos转化成location结束,geos:{}",geos);
        //3.调用geoOps操作对象的api
        geoOps.add("charging:all:stations.geo",geos);

    }
    @Override
    public List<StationGeoPO> nearStations(NearStationsQuery query) {
        List<StationGeoPO> pos=null;
        //1.第一部分数据 stationId stationLng stationLat distance 从redis获取
        GeoOperations<String,Integer> geoOps = redisTempalte.opsForGeo();
        //2.调用范围查找radius方法 georadius lng lat raidus withcoord withdist
        //2.1组织调用查询参数circle 包含center radius
        Point center=new Point(query.getLongitude(),query.getLatitude());
        Distance radius=new Distance(query.getRadius());
        Circle circle=new Circle(center,radius);
        //2.2 携带查询选项 withcoord withdist
        RedisGeoCommands.GeoRadiusCommandArgs arguments=
                RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs();
        arguments.includeCoordinates().includeDistance();
        //3.调用radius方法获取redis返回结果 GeoResults
        GeoResults<RedisGeoCommands.GeoLocation<Integer>> allResults =
                geoOps.radius("charging:all:stations.geo", circle, arguments);
        //4.解析当前查询到的结果
        //4.1先拿到allResults命中的元素集合List
        List<GeoResult<RedisGeoCommands.GeoLocation<Integer>>> geoDatas = allResults.getContent();
        //4.2如果geoDatas非空 可以循环遍历 解析转化成pos
        if (geoDatas!=null&&geoDatas.size()>0){
            log.debug("当前查询范围场站,入参:{},命中场站个数:{}",query,geoDatas.size());
            //对geoDatas解析 封装pos元素 pos可以进行初始化
            pos=new ArrayList<>();
            for (GeoResult<RedisGeoCommands.GeoLocation<Integer>> geoData : geoDatas) {
                //准备好返回给业务层的一个po元素
                StationGeoPO po=new StationGeoPO();
                //从geoData拿到距离
                Double distance=geoData.getDistance().getValue();
                //从geoData拿到元素坐标 x--lng y--lat
                Double lng=geoData.getContent().getPoint().getX();
                Double lat=geoData.getContent().getPoint().getY();
                //geoData拿到元素值 stationId
                Integer stationId = geoData.getContent().getName();
                //po缺少的属性stationName stationStatus 从数据库读取
                ChargingStationPO stationPO=getStationById(stationId);
                String stationName = stationPO.getStationName();
                Integer stationStatus = stationPO.getStationStatus();
                po.setStationId(stationId);
                po.setStationLat(lat);
                po.setStationLng(lng);
                po.setDistance(new BigDecimal(distance));
                po.setStationName(stationName);
                po.setStationStatus(stationStatus);
                pos.add(po);
            }
        }
        return pos;
    }
    @Override
    public ChargingStationPO getStationById(Integer stationId) {
        //拿到直接操作value的客户端对象
        ValueOperations<String,ChargingStationPO> valueOps = redisTempalte.opsForValue();
        //1.准备读取缓存 缓存数据结构是string结构,需要key和value设计
        String stationCacheKey="charging:station:"+stationId+".cache";
        ChargingStationPO stationPo = valueOps.get(stationCacheKey);
        //判断命中
        if (stationPo==null){
            log.debug("缓存未命中,开始查询数据库,stationId:{}",stationId);
            //2.读取持久层数据库
            //select station_name,station_status from station where id=?
            stationPo = stationMapper.selectById(stationId);
            //3.回填缓存保证下次 可以命中
            valueOps.set(stationCacheKey,stationPo);
        }
        return stationPo;
    }
    @Override
    public String getStationName(Integer stationId) {
        return "";
    }

    @Override
    public List<ChargingGunInfoPO> getStationGuns(Integer stationId) {
        //select gun.* from charging_gun_info gun
        //left join charging_pile_info pile
        //on gun.pile_id=pile.id
        //where pile.station_id=#{}
        //先有得BaseMapper方法不太支持关联查询 所以我们自定义sql方法
        return gunMapper.selectByStationId(stationId);
    }

    @Override
    public Boolean updateGunStatus(Integer gunId, Integer status) {
        return null;
    }

    @Override
    public void saveStation(StationCanalPO stationCanalPO) {

    }

    @Override
    public void updateStation(StationCanalPO before, StationCanalPO after) {

    }

    @Override
    public void deleteStation(StationCanalPO stationCanalPO) {

    }

    @Override
    public Long countGunByIdAndStatus(Integer gunId, Integer status) {
        return 0l;
    }


}
